#include "matrix3D.hxx"
#include "matrix3D.cuh"
#include <iostream>
#include <time.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <sys/types.h>
#include <windows.h>
#include <iostream>
#include <math.h>
#include "etelonny.hxx"

#define HISTOGRAM_BINS 256

using namespace std;

void  Matrix3D::smooth()
{
    controlSmooth(&_data[0], &_data[0], _xCount, _yCount, _zCount);
}

void Matrix3D::transform(std::vector<int> classes)
{
    short* src = &_data[0];
    short *dist = new short[modelSize()];
    
    cuda_classify(src, src, modelSize(),&classes[0], classes.size());
#if (_DEBUG)
    std::cout << "histogram classes count is " << classes.size() << std::endl;
    std::cout << "First " << min(modelSize(),128) << " of transformed matrix" << std::endl;
    for(int i = 0; i < min(modelSize(),128); i++)
    {
        std::cout << src[i] << " with index [" << i  << "]"<< std::endl;
    }
#endif

}

Histogram Matrix3D::histogram(int warpCount, Histogram::Type type, Matrix3D::CudaStrategy strategy)
{
#if (_DEBUG)
    std::cout << "Histogram generation for matrix[" << _xCount << ", " << _yCount << ", " << _zCount << "]" <<  std::endl;
    std::cout << "using " << ((strategy)?"optimized":"simple") << " strategy " << std::endl;
    std::cout << "Histigram type is " << ((type == Histogram::DESTRIBUTION)?"one dimensional by distribution":
        ((type == Histogram::GRADIENT)?"one dimensional by gradient aproximtion":"2D by distribution and gradient aproximtion")) << std::endl;
#endif
    Histogram h(HISTOGRAM_BINS,type);
#if (_DEBUG)
    std::cout << "cuda function call..." << std::endl;
#endif
    switch(type)
    {
    case Histogram::DESTRIBUTION:
        {
            cuda_histogram1D(&_data[0], modelSize(), h.bins(), HISTOGRAM_BINS, warpCount);
            break;
        }
    case Histogram::GRADIENT:
        {
            Matrix3D sobel = this->sobel(Matrix3D::DX);
            cuda_histogram1D(&(sobel._data[0]), modelSize(), h.bins(), HISTOGRAM_BINS, warpCount);
            break;
        }
    case (Histogram::MULTI):
        {
            Matrix3D sobel = this->sobel(Matrix3D::DX);
            cuda_histogram2D(&_data[0], &(sobel._data[0]), modelSize(), h.bins(), HISTOGRAM_BINS, warpCount);
            break;
        }
    }
    
#if (_DEBUG)
    std::cout << "____________________________________" << std::endl;
    std::cout << "Result histogram" << std::endl;

    unsigned int *hist = h.bins();
    int sum=0;
    for(int i = 0; i<HISTOGRAM_BINS; i++)
    {
        std::cout << hist[i] << " for bin with index [" << i  << "]"<< std::endl;
        sum += hist[i];
    }
    std::cout <<"Generated histogram sum for all bins is " << sum  << ". The array size is "<< modelSize() << std::endl;
#endif
    return h;
}

Matrix3D Matrix3D::sobel(Matrix3D::Derivative type)
{
#if (_DEBUG)
    std::cout << "Sobel derivative for matrix[" << _xCount << ", " << _yCount << ", " << _zCount << "]" <<  std::endl;
    std::cout << "by " << ((type == 0)?"X":((type == 1)?"Y":"Z")) << " coordinate" << std::endl;
#endif
    short* src = &_data[0];
    short *dist = new short[modelSize()];
    
    cuda_sobel(src, dist, _xCount, _yCount, _zCount, type);

    Ptr<short> deriv(dist);
    Matrix3D m(_xCount, _yCount, _zCount, deriv);
    return m;
}

void Matrix3D::store(char* filePath)
{
    float vok = 1;
    FILE * source=fopen(filePath ,"wb");
    fwrite(&_xCount,sizeof(int),1,source);
    fwrite(&_yCount,sizeof(int),1,source);
    fwrite(&_zCount,sizeof(int),1,source);
    fwrite(&vok,sizeof(float),1,source);
    fwrite(&vok,sizeof(float),1,source);
    fwrite(&vok,sizeof(float),1,source);

    for(int i=0; i<_xCount*_yCount*_zCount; i++)
         fwrite(&_data[i],sizeof(short),1,source);

    fclose(source);
}

Matrix3D Matrix::parse(std::string path)
{
    FILE* source;
    short* data;
    source=fopen(path.c_str(),"rb");
    int number;
    int m=4;
    short* list;
    list=new short[m];
    if(source==NULL) 
    {
        printf("File does not found");
        getchar();
        return 1;
    }

    int size_x,size_y,size_z;
    float vok_x,vok_y,vok_z;

    number=fread(&size_x, sizeof(int), 1, source);
    number=fread(&size_y, sizeof(int), 1, source);
    number=fread(&size_z, sizeof(int), 1, source);

    number=fread(&vok_x, sizeof(float), 1, source);
    number=fread(&vok_y, sizeof(float), 1, source);
    number=fread(&vok_z, sizeof(float), 1, source);

    data=new short[size_x*size_y*size_z];
    int minThreshold = 0, maxThreshold = 0;
    int k=0;
    while(feof(source)==false)
    {
        number=fread( list, sizeof(short), m, source );
        if(number==0) break;

        for(int i=0;i<number;i++)
            data[k+i]+=list[i];
        k+=number;
    }
    fclose(source);

    Ptr<short> ptr(data);
    Matrix3D mz(size_x,size_y,size_z, ptr);
    return mz;
}

Matrix3D Matrix::generateUniform(int x, int y, int z)
{
    int size =x*y*z;
    short * data=new short[size];
    srand ( time(NULL) );

    controlFunction(data,size);

    Ptr<short> ptr(data);
    //use the defailte thresholds
    Matrix3D m(x,y,z, ptr);
    return m;
}
